package model;

import controller.GuardarSimulacion;
import controller.WorkerSimulacion;
import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JOptionPane;
import view.EstadoSimulacion;
import view.Menu;
import view.SimulacionMapa2;

// ESTA CLASE CONTIENE TODO LO NECESARIO PARA LLEVAR A CABO Y GESTIONAR LA SIMULACION
public class Simulacion {

    // ATRIBUTOS
    private String directorio_archivos; //el directorio donde estan los archivos con la data historica (leidos de la ventana)
    private float minutos_sim;  //la cantidad de minutos que se avanzara en cada iteracion de la simulacion
    private Map<String, Map<String, Integer>> arbolRatios = new LinkedHashMap<String, Map<String, Integer>>();  // arbol que contiene los ratios hacia cada destino, agrupados por origen  
    private Map<String, Map<String, Integer>> arbolPaquetes = new LinkedHashMap<String, Map<String, Integer>>();  // arbol que contiene los ratios hacia cada destino, agrupados por origen    
    private List<Nodo> listaNodos = new ArrayList<Nodo>();    //lista de nodos del grafo
    private List<Arista> listaAristas = new ArrayList<Arista>(); //lista de aristas del grafo
    private Date fechaHoraSim;  // la fecha y hora de la simulacion
    private Date ultimaFechaBorradoVuelos;  // la ultima fecha en que se eliminaron vuelos
    private Date ultimaFechaCreacionVuelos;  // la ultima fecha de vuelos creados
    private List<Paquete> listaPaquetesARutear = new ArrayList<Paquete>();  //lista de paquetes que deben ser asignados a vuelos
    private Date fechaInicioSim;    // fecha de inicio de la simulacion
    private int numPaquetes = 10;   // el numero de iteraciones que ha tenido la simulacion
    private DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
    private Date fechaSimulacion;
    private WorkerSimulacion worker;

    // CONSTRUCTORES
    
    public Simulacion(String directorio_archivos) {
        this.directorio_archivos = directorio_archivos;
    }
    
    public Simulacion(String directorio_archivos, WorkerSimulacion worker) {
        this.directorio_archivos = directorio_archivos;
        this.worker = worker;
    }
    
    public Simulacion() {
    }

    // SETTERS Y GETTERS
    public WorkerSimulacion getWorker() {
        return worker;
    }

    public void setWorker(WorkerSimulacion worker) {
        this.worker = worker;
    }
    
    public Date getFechaSimulacion() {
        return fechaSimulacion;
    }

    public void setFechaSimulacion(Date fechaSimulacion) {
        this.fechaSimulacion = fechaSimulacion;
    }
    
    public Map<String, Map<String, Integer>> getArbolPaquetes() {
        return arbolPaquetes;
    }

    public void setArbolPaquetes(Map<String, Map<String, Integer>> arbolPaquetes) {
        this.arbolPaquetes = arbolPaquetes;
    }    
    
    public int getNumPaquetes() {
        return numPaquetes;
    }

    public void setNumPaquetes(int numPaquetes) {
        this.numPaquetes = numPaquetes;
    }
    
    
    public Map<String, Map<String, Integer>> getArbolRatios() {
        return arbolRatios;
    }

    public void setArbolRatios(Map<String, Map<String, Integer>> arbolRatios) {
        this.arbolRatios = arbolRatios;
    }

    public List<Nodo> getListaNodos() {
        return listaNodos;
    }

    public void setListaNodos(List<Nodo> listaNodos) {
        this.listaNodos = listaNodos;
    }

    public List<Arista> getListaAristas() {
        return listaAristas;
    }

    public void setListaAristas(List<Arista> listaAristas) {
        this.listaAristas = listaAristas;
    }

    public List<Paquete> getListaPaquetesARutear() {
        return listaPaquetesARutear;
    }

    public void setListaPaquetesARutear(List<Paquete> listaPaquetesARutear) {
        this.listaPaquetesARutear = listaPaquetesARutear;
    }

    public void setUltimaFechaCreacionVuelos(Date ultimaFechaCreacionVuelos) {
        this.ultimaFechaCreacionVuelos = ultimaFechaCreacionVuelos;
    }

    public String getDirectorio_archivos() {

        return directorio_archivos;

    }

    public void setDirectorio_archivos(String directorio_archivos) {
        this.directorio_archivos = directorio_archivos;
    }

    public float getMinutos_sim() {
        return minutos_sim;
    }

    public void setMinutos_sim(float minutos_sim) {
        this.minutos_sim = minutos_sim;
    }

    public Date getFechaHoraSim() {
        return fechaHoraSim;
    }

    public void setFechaHoraSim(Date fechaHoraSim) {
        this.fechaHoraSim = fechaHoraSim;
    }

    public Date getUltimaFechaBorradoVuelos() {
        return ultimaFechaBorradoVuelos;
    }

    public void setUltimaFechaBorradoVuelos(Date ultimaFechaBorradoVuelos) {
        this.ultimaFechaBorradoVuelos = ultimaFechaBorradoVuelos;
    }

    public Date getUltimaFechaCreacionVuelos() {
        return ultimaFechaCreacionVuelos;
    }

    public Date getFechaInicioSim() {
        return fechaInicioSim;
    }

    public void setFechaInicioSim(Date fechaInicioSim) {
        this.fechaInicioSim = fechaInicioSim;
    }

    
    
    
    // METODOS
    
    // metodo que se encarga de toda la ejecucion de la simulacion
    public void ejecutarSimulacion() {

        try {
            
            EstadoSimulacion estado = new EstadoSimulacion();
            estado.setAlwaysOnTop(true);
            estado.setVisible(true);
            
            //System.out.println("Carga de estructuras");
            // carga todas las estructuras de datos necesarias para la simulacion
            Carga.cargarEstructurasDesdeBD(this);
            Carga.limpiarHistorialVuelos(this);
            
            //prueba del metodo anterior (a borrar mas adelante)
            System.out.println("minutos_sim = " + this.minutos_sim);
            System.out.println("Num. nodos = " + this.getListaNodos().size());
            System.out.println("Num. aristas = " + this.getListaAristas().size());
            
            //System.out.println("Obtencion de Ratios");
            // lee archivos de data historica y obtiene los ratios de crecimiento
            estado.getLblEstado().setText("Obteniendo ratios de crecimiento ...");
            Ratios.obtenerRatios(this);
            this.getArbolRatios().clear();
            Ratios.obtenerRatiosDesdeBD(this);
                        
            // genero arbol que almacenara el numero de paquetes a generar
            Generacion.rellenarArbolPaquetes(this);
            
            //System.out.println("Seteo de fechas");
            // seteo la fecha y hora de inicio de la simulacion, y la ultima en que se borraron vuelos
            estado.getLblEstado().setText("Configurando parámetros de la simulación ...");
            Calendar cal = Calendar.getInstance();
            cal.set(Calendar.HOUR_OF_DAY, 0);
            cal.set(Calendar.MINUTE, 0);
            cal.set(Calendar.SECOND, 0);
            cal.set(Calendar.MILLISECOND, 0);
            fechaHoraSim = cal.getTime();
            ultimaFechaBorradoVuelos = cal.getTime();
            ultimaFechaCreacionVuelos = fechaHoraSim;
            fechaInicioSim =  fechaHoraSim;
            fechaSimulacion = new Date();
            
            
            // muestro la ventana de simulacion con el mapa
            
           //SimulacionMapa2 ventanaSimuMapa = new SimulacionMapa2(this);
            SimulacionMapa2 ventanaSimuMapa = new SimulacionMapa2(this,this.worker);
            
            FechasDeVuelos.GenerarAnho(this);
            
            estado.getLblEstado().setText("Iniciando simulación ...");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException ex) {
                Logger.getLogger(Simulacion.class.getName()).log(Level.SEVERE, null, ex);
            }
            estado.dispose();
            
            //System.out.println("Bucle");
            // empieza el bucle de la simulacion
        while (!this.colapso(this)){  // mientras no se haya colapsado ningun almacen
             
            //System.out.println("Imprimir fecha");
            System.out.println(dateFormat.format(fechaHoraSim));  
            
//            System.out.println("Agregar fechas a vuelos");
            // si faltan 3 dias o menos para que se llegue a la ultima fecha de vuelo generada, generar 1 semana mas a partir de esa ultima fecha
            //FechasDeVuelos.agregarFechasAVuelos(this);
                        
//            System.out.println("Revisar Llegada de Vuelos");
            //reviso aquellos vuelos que llegaron a su destino a esta fecha y hora de la simulacion, actualizo capacidades de esos vuelos y de aeropuertos
            Revision.revisarLLegadaDeVuelos(this);
            
//            System.out.println("Generar Paquetes");
            // genero mas paquetes
            Generacion.generarPaquetes(this);
            System.out.println("Num. paquetes a rutear: " + this.listaPaquetesARutear.size());
            
//            System.out.println("Algoritmo");
            // aplico el algoritmo a cada uno de esos paquetes para asignarles vuelos en ciertas fechas
            for (int i=0; i<this.listaPaquetesARutear.size(); i++){
//                System.out.println("i: "+i + ", origen: " + listaPaquetesARutear.get(i).getCiudadByIdCiudadOrigen().getCiudad() + ", destino: " + listaPaquetesARutear.get(i).getCiudadByIdCiudadDestino().getCiudad());
                //System.out.println(this.listaPaquetesARutear.get(i).getCiudadByIdCiudadOrigen().getCiudad() + " " +
                //            this.listaPaquetesARutear.get(i).getCiudadByIdCiudadDestino().getCiudad());
                Algoritmo.algoritmoAEstrella(this,this.listaPaquetesARutear.get(i));
                //System.out.println("i: " + i);
            }
            
//            System.out.println("Revisar partida de vuelos");
            // reviso aquellos vuelos que partieron de su origen a esta fecha y hora de la simulacion
            Revision.revisarPartidaDeVuelos(this);
            
//            System.out.println("Dibujo Mapa");
            // actualizo el mapa grafico de la simulacion
            ventanaSimuMapa.actualizarMapa(this);
            
//            System.out.println("Actualizo fecha de simulacion");
            // aumento el tiempo a la simulacion   
            this.fechaHoraSim = new Date(this.fechaHoraSim.getTime()+((int)(Math.round(minutos_sim)))*60*1000);
            
            //System.out.println("Eliminar Fechas de vuelos");
            // si la ultima fecha en que se borraron vuelos fue hace 7 dias, borro vuelos desde esa fecha hasta 5 dias despues
            //FechasDeVuelos.eliminarFechasAVuelos(this);
            
            //System.out.println("Dormir");
                try {
                    // duermo por 2 segundos
                    Thread.sleep(1000);
                } catch (InterruptedException ex) {
                    //Logger.getLogger(Simulacion.class.getName()).log(Level.SEVERE, null, ex);
                    //System.out.println("sleep");
                    this.worker.cancel(true);
                    ventanaSimuMapa.dispose();
                    Thread.currentThread().interrupt();
                    return;
                    //new Menu().setVisible(true);
                }
                
            
        }
          
        // guardo resultados de la simulacion en la BD
        GuardarSimulacion.guardarSimuEnBD(this);
        
        //ventana final para preguntar si usuario desea ver el reporte
//        int dialogButton = JOptionPane.YES_NO_OPTION;
//        int dialogResult = JOptionPane.showConfirmDialog (null, "La simulación ha concluido ¿Desea ver el reporte en este momento?","Confirmación",dialogButton);
//        if(dialogResult == JOptionPane.YES_OPTION){ // si el usuario eligio "SI"
//            
//         }
//        if (dialogResult==JOptionPane.NO_OPTION)    // si el usuario eligio "NO"
//        {
//           
//        }
        new Menu().setVisible(true);
        JOptionPane.showMessageDialog(null, "Simulación finalizada con éxito.", "Simulación", JOptionPane.INFORMATION_MESSAGE);
        
        } catch (IOException ex) {
            Logger.getLogger(Simulacion.class.getName()).log(Level.SEVERE, null, ex);
        }
        
    }

    
    
    
    
    
    
    
    // metodo que se encarga de toda la ejecucion de la simulacion DESDE LA BD
    public void ejecutarSimulacionDesdeBD()  {

        try {
            
            EstadoSimulacion estado = new EstadoSimulacion();
            estado.setAlwaysOnTop(true);
            estado.setVisible(true);
            
            //System.out.println("Carga de estructuras");
            // carga todas las estructuras de datos necesarias para la simulacion
            Carga.cargarEstructurasDesdeBD(this);
            Carga.limpiarHistorialVuelos(this);
            
            //prueba del metodo anterior (a borrar mas adelante)
            System.out.println("minutos_sim = " + this.minutos_sim);
            System.out.println("Num. nodos = " + this.getListaNodos().size());
            System.out.println("Num. aristas = " + this.getListaAristas().size());
            
            //System.out.println("Obtencion de Ratios");
            // lee archivos de data historica y obtiene los ratios de crecimiento
            estado.getLblEstado().setText("Obteniendo ratios de crecimiento ...");
            Ratios.obtenerRatiosDesdeBD(this);
                        
            // genero arbol que almacenara el numero de paquetes a generar
            Generacion.rellenarArbolPaquetes(this);
            
            //System.out.println("Seteo de fechas");
            // seteo la fecha y hora de inicio de la simulacion, y la ultima en que se borraron vuelos
            estado.getLblEstado().setText("Configurando parámetros de la simulación ...");
            Calendar cal = Calendar.getInstance();
            cal.set(Calendar.HOUR_OF_DAY, 0);
            cal.set(Calendar.MINUTE, 0);
            cal.set(Calendar.SECOND, 0);
            cal.set(Calendar.MILLISECOND, 0);
            fechaHoraSim = cal.getTime();
            ultimaFechaBorradoVuelos = cal.getTime();
            ultimaFechaCreacionVuelos = fechaHoraSim;
            fechaInicioSim =  fechaHoraSim;
            fechaSimulacion = new Date();
            
            
            // muestro la ventana de simulacion con el mapa
            
           //SimulacionMapa2 ventanaSimuMapa = new SimulacionMapa2(this);
            SimulacionMapa2 ventanaSimuMapa = new SimulacionMapa2(this,this.worker);
            
            FechasDeVuelos.GenerarAnho(this);
            
            estado.getLblEstado().setText("Iniciando simulación ...");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException ex) {
                Logger.getLogger(Simulacion.class.getName()).log(Level.SEVERE, null, ex);
            }
            estado.dispose();
            
            //System.out.println("Bucle");
            // empieza el bucle de la simulacion
        while (!this.colapso(this)){  // mientras no se haya colapsado ningun almacen
             
            //System.out.println("Imprimir fecha");
            System.out.println(dateFormat.format(fechaHoraSim));  
            
//            System.out.println("Agregar fechas a vuelos");
            // si faltan 3 dias o menos para que se llegue a la ultima fecha de vuelo generada, generar 1 semana mas a partir de esa ultima fecha
            //FechasDeVuelos.agregarFechasAVuelos(this);
                        
//            System.out.println("Revisar Llegada de Vuelos");
            //reviso aquellos vuelos que llegaron a su destino a esta fecha y hora de la simulacion, actualizo capacidades de esos vuelos y de aeropuertos
            Revision.revisarLLegadaDeVuelos(this);
            
//            System.out.println("Generar Paquetes");
            // genero mas paquetes
            Generacion.generarPaquetes(this);
            System.out.println("Num. paquetes a rutear: " + this.listaPaquetesARutear.size());
            
//            System.out.println("Algoritmo");
            // aplico el algoritmo a cada uno de esos paquetes para asignarles vuelos en ciertas fechas
            for (int i=0; i<this.listaPaquetesARutear.size(); i++){
//                System.out.println("i: "+i + ", origen: " + listaPaquetesARutear.get(i).getCiudadByIdCiudadOrigen().getCiudad() + ", destino: " + listaPaquetesARutear.get(i).getCiudadByIdCiudadDestino().getCiudad());
                //System.out.println(this.listaPaquetesARutear.get(i).getCiudadByIdCiudadOrigen().getCiudad() + " " +
                //            this.listaPaquetesARutear.get(i).getCiudadByIdCiudadDestino().getCiudad());
                Algoritmo.algoritmoAEstrella(this,this.listaPaquetesARutear.get(i));
                //System.out.println("i: " + i);
            }
            
//            System.out.println("Revisar partida de vuelos");
            // reviso aquellos vuelos que partieron de su origen a esta fecha y hora de la simulacion
            Revision.revisarPartidaDeVuelos(this);
            
//            System.out.println("Dibujo Mapa");
            // actualizo el mapa grafico de la simulacion
            ventanaSimuMapa.actualizarMapa(this);
            
//            System.out.println("Actualizo fecha de simulacion");
            // aumento el tiempo a la simulacion   
            this.fechaHoraSim = new Date(this.fechaHoraSim.getTime()+((int)(Math.round(minutos_sim)))*60*1000);
            
            //System.out.println("Eliminar Fechas de vuelos");
            // si la ultima fecha en que se borraron vuelos fue hace 7 dias, borro vuelos desde esa fecha hasta 5 dias despues
            //FechasDeVuelos.eliminarFechasAVuelos(this);
            
            //System.out.println("Dormir");
                try {
                    // duermo por 2 segundos
                    Thread.sleep(1000);
                } catch (InterruptedException ex) {
                    //Logger.getLogger(Simulacion.class.getName()).log(Level.SEVERE, null, ex);
                    //System.out.println("sleep");
                    this.worker.cancel(true);
                    ventanaSimuMapa.dispose();
                    Thread.currentThread().interrupt();
                    return;
                    //new Menu().setVisible(true);
                }
                
            
        }
          
        // guardo resultados de la simulacion en la BD
        GuardarSimulacion.guardarSimuEnBD(this);
        
        //ventana final para preguntar si usuario desea ver el reporte
//        int dialogButton = JOptionPane.YES_NO_OPTION;
//        int dialogResult = JOptionPane.showConfirmDialog (null, "La simulación ha concluido ¿Desea ver el reporte en este momento?","Confirmación",dialogButton);
//        if(dialogResult == JOptionPane.YES_OPTION){ // si el usuario eligio "SI"
//            
//         }
//        if (dialogResult==JOptionPane.NO_OPTION)    // si el usuario eligio "NO"
//        {
//           
//        }
        new Menu().setVisible(true);
        JOptionPane.showMessageDialog(null, "Simulación finalizada con éxito.", "Simulación", JOptionPane.INFORMATION_MESSAGE);
        
        } catch (IOException ex) {
            Logger.getLogger(Simulacion.class.getName()).log(Level.SEVERE, null, ex);
        }
                     
    }
    
    // metodo para comprabar si algun aeropuerto ha colapsado
    public boolean colapso(Simulacion simu) {
        boolean hayColapso = false;

        for (int i=0; i < simu.getListaNodos().size(); i++){    // para cada uno de los nodos de la lista de nodos
            if (simu.getListaNodos().get(i).getCiudad().getCapDisp() < 0){ // si la ciudad de ese nodo ya no tiene capacidad
                return true;    // colapso
            }
        }
        
        return hayColapso;  // no colapso
    }
    
    // metodo para realizar el analisis de data historica y guardar ratios en BD
    public void cargaDataHistorica(){
        
         // carga todas las estructuras de datos necesarias para la obtencion de ratios
        Carga.cargarEstructurasDesdeBD(this);
        
        try {
            // lee archivos de data historica y obtiene los ratios de crecimiento, luego los guarda en BD
            Ratios.obtenerRatios(this);
        } catch (IOException ex) {
            Logger.getLogger(Simulacion.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    
}